if not ticks then

dofile (ScriptsRootPath .. "lib\\lib.lua")


ticks = {
		data = {},
		OnTick = {},
		clean_size = 10,
		pressure_rows = 5,
		average_period = 600		-- sec
	}


function ticks:add(tick)
	if tick and type(tick) == "table"
		and self.data[tick.sec_code]
		and type(tick.time) == "string"
		and (#self.data[tick.sec_code].times == 0
			or tick.time >= self.data[tick.sec_code].times[#self.data[tick.sec_code].times]
		)
	then
		if #self.data[tick.sec_code].index > 0 then
			tick.chain = self.data[tick.sec_code].index[#self.data[tick.sec_code].index]
		end
		
		if not self.data[tick.sec_code].ticks[tick.time] then
			self.data[tick.sec_code].ticks[tick.time] = {}
			table.sinsert(self.data[tick.sec_code].times, tick.time)
		end
		
		table.sinsert(self.data[tick.sec_code].ticks[tick.time], tick)
		table.sinsert(self.data[tick.sec_code].index, tick)
		
		return tick
	end
end


function ticks:get(sec_code, t)
	local d
	if type(sec_code) == "table" then		-- get previous in chain
		if type(t) == "string" then			-- systime
			return self:get(sec_code.sec_code, t)
		else
			if not t then t = 1 end
			d = sec_code
			for i = 1, int(t) do
				if not d then break end
				d = d.chain
			end
		end
		
		if d then
			return d, self.data[d.sec_code].ticks[d.time]
		end
	elseif self.data[sec_code] then
		if not t then
			return #self.data[sec_code].index - 1,	-- last index possible, 0 - last tick
				self.data[sec_code].times[#self.data[sec_code].times]
		end
		
		if type(t) == "string" then		-- systime
			local dd = self.data[sec_code].ticks[t]
			
			if dd then
				if self.data[sec_code].last_time == t then
					local l = int(self.data[sec_code].last) + 1
					d = dd[l]
					
					if d then
						self.data[sec_code].last = l
						return d, dd
					end
				end
			
				self.data[sec_code].last_time = t
				self.data[sec_code].last = 1
				
				return dd[1], dd
			end
		else	-- index from the end, 0 - last tick
			d = self.data[sec_code].index[#self.data[sec_code].index - t]
			
			if d then
				return d, self.data[sec_code].ticks[d.time]
			end
		end
	end
end


function ticks:seconds(tick, i)
	if i then		-- sec_code
		return self:seconds((self:get(tick, i)))
	elseif type(tick) == "table" then
		return self:seconds(tick.time)
	end
	
	return lib:tickseconds(tick)
end


function ticks:GetCleanPeriod(period)
	return math.max(int(self.average_period),
		int(self.clean_period),
		int(period)
	)
end


function ticks:clean(sec_code, period)
	period = self:GetCleanPeriod(period or sec_code)
	
	if period <= 0 then return end
	local count = 0
	
	if type(sec_code) == "string" then
		local t = self:seconds(sec_code, 0) - period
		local n = self.clean_size
		
		if n > #self.data[sec_code].times then n = #self.data[sec_code].times end
		
		for i = 1, n do
			if lib:tickseconds(self.data[sec_code].times[1]) >= t then break end
			
			for j = 1, #self.data[sec_code].ticks[self.data[sec_code].times[1]] do
				if #self.data[sec_code].index > 1 then
					self.data[sec_code].index[2].chain = nil
				end
				table.sremove(self.data[sec_code].index, 1)
			end
			
			self.data[sec_code].ticks[self.data[sec_code].times[1]] = nil
			table.sremove(self.data[sec_code].times, 1)
			count = count + 1
		end
	else
		for sec_code in pairs(self.data) do
			count = count + self:clean(sec_code, period)
		end
		
		if count > 0 then
			collectgarbage("collect")
		end
	end
	
	return count
end


function ticks:GetSpread(tick, q)
	if q
		and type(q.bid) == "table"
		and type(q.offer) == "table"
		and #q.bid > 0
		and #q.offer > 0
	then
		table.sort(q.bid, self.CompBids)
		table.sort(q.offer, self.CompOffers)
		
		tick.price_bid = q.bid[1].price
		tick.price_offer = q.offer[1].price
		tick.volume_bid = q.bid[1].quantity
		tick.volume_offer = q.offer[1].quantity
		
		return tick
	end
end


function ticks:CalculatePressure(tick, q, rows)
	if not rows or rows <= 0 then
		rows = int(self.pressure_rows)
	end
	
	if self:GetSpread(tick, q) then
		local b = 0
		
		for i, v in ipairs(q.bid) do
			if i > rows then break end
			b = b + v.quantity
		end
		
		local o = 0
		
		for i, v in ipairs(q.offer) do
			if i > rows then break end
			o = o + v.quantity
		end
		
		tick.pressure_bid = int(b)
		tick.pressure_offer = int(o)
		
		return tick
	end
end


function ticks.CompBids(w1, w2)
    if (w2.price < w1.price) then
        return true
    end
end


function ticks.CompOffers(w1, w2)
    if (w2.price > w1.price) then
        return true
    end
end


function ticks:CalculateAverage(tick, period)		-- for last tick added
	if not period or period <= 0 then
		period = int(self.average_period)
	end
		
	if tick then
		tick.average_pressure_bid = tick.pressure_bid
		tick.average_pressure_offer = tick.pressure_offer
		tick.average_volume = tick.volume
			
		if self.data[tick.sec_code] and period > 0 then
			local d
			local tmin = self:seconds(tick) - period
			local ab = tick.pressure_bid
			local ao = tick.pressure_offer
			local av = tick.volume
			local n = 1
			local m = self:get(tick.sec_code)
			
			for i = 1, m do
				d = self:get(tick.sec_code, i)
				
				if tmin > self:seconds(d) then
					break
				end
				
				ab = ab + d.pressure_bid
				ao = ao + d.pressure_offer
				av = av + d.volume
				n = n + 1
			end
			
			tick.average_pressure_bid = round(ab / n)
			tick.average_pressure_offer = round(ao / n)
			tick.average_volume = round(av / n)
		end
	end
					
	return tick
end	


function ticks:line2tick(line, sec_code)
	if sec_code and lib:isticktime(line) then
		local d = split(line, "\t")
		
		if #d >= 10 then
			return {
				sec_code = sec_code,
				time = d[1],
				price = tonumber(d[2]),
				volume = int(d[3]),
				side = d[4] == "true",
				price_bid = tonumber(d[5]),
				volume_bid = int(d[6]),
				price_offer = tonumber(d[7]),
				volume_offer = int(d[8]),
				pressure_bid = int(d[9]),
				pressure_offer = int(d[10])
			}
		end
	end
end	


function ticks:load(sec_code, now)
	self.data[sec_code] = {ticks = {}, times = {}, index = {}}
	--if not Settings then return self.data[sec_code] end		-- load in indicator only
	if self.no_load then return self.data[sec_code] end			-- no load on test
	
	local file = lib.path.ticks .. lib:FormatDate() .. "." .. sec_code
	local t, tick
	
	local hf = io.open(file, "r")
	
	if hf then
		if not lib:exists(lib.path.init .. "all_ticks.flag") then
			local p = hf:seek("end")
			
			if Settings then
				p = p - 102400 -- 51200 -- 209600 --
			else
				p = p - 419200 -- 1048576 --
			end
			
			if p < 0 then p = 0 end
			
			hf:seek("set", p)
		end
		
		local data = hf:read("*a")
		hf:close()
		
		local tmin
		
		if now then tmin = lib:tickseconds(now) - self:GetCleanPeriod() end
		
		if data then
			for v in string.gmatch(data, "([^\n]+)\n") do
				tick = self:line2tick(v, sec_code)
				
				if tick and (not tmin or lib:tickseconds(tick.time) >= tmin) then
					tick = self:CalculateAverage(
						self:add(tick)
					)
					
					if not t and tick then t = tick end
				end
			end
		end
	end
	
	if processor and processor.log and t then
		processor:log("Ticks " .. sec_code .. " loaded " .. (self:get(sec_code) + 1) .. " from " .. t.time .. " to " .. tick.time .. " period " .. self:GetCleanPeriod())
	end
	
	return self.data[sec_code]
end


function ticks:GetSide(tick)
	if tick then
		local tr, t
		
		for i = getNumberOf("all_trades") - 1, 0, -1 do
			tr = getItem("all_trades", i)
			
			if not tr then
				if not self.log_side then
					self.log_side = true
					message("getItem all_trades error "
						.. i .. " " .. getNumberOf("all_trades")
					)
				end
				
				break
			end
			if tr.sec_code == tick.sec_code then
				t = lib:ticktime(tr.datetime)
				
				if t < tick.time then
					if not self.log_side then
						self.log_side = true
						message("GetSide time error "
							.. " " .. tr.sec_code
							.. i .. " " .. getNumberOf("all_trades")
							.. " " .. t
							.. " " .. tick.time
						)
					end
					
					break
				end
				if t == tick.time and tr.price == tick.price and tr.qty == tick.volume then
					tick.trade = i
					
					if bit.test(tr.flags, 0) then
						tick.side = false
					elseif bit.test(tr.flags, 1) then
						tick.side = true
					end
					
					break
				end
			end
		end
	end
end


function ticks:GetTrend(tick, trend, period)
	period = int(period) * 60
	
	if period > 0 and (not trend.time or string.sub(trend.time, 1, 5) ~= string.sub(tick.time, 1, 5)) then		-- every 10 seconds
		local pmax = tick.price
		local pmin = tick.price
		local is_long
		local tmin = lib:tickseconds(tick.time) - period
		local t = ticks:get(tick)
		
		while t and lib:tickseconds(t.time) >= tmin do
			if t.price then
				if pmin > t.price then
					pmin = t.price
					is_long = true
				end
				
				if pmax < t.price then
					pmax = t.price
					is_long = false
				end
			end
			
			t = ticks:get(t)
		end
		
		trend.time = tick.time
		trend.pmin = pmin
		trend.pmax = pmax
		trend.is_long = is_long
		trend.percent = round((pmax - pmin) * 100 / pmin, 2)
	end
end


if not Settings then	-- not an indicator


	dofile (ScriptsRootPath .. "processor\\processor.lua")
	dofile (ScriptsRootPath .. "processor\\saver.lua")

	ticks.level_II = {}
	ticks.saver = {}
	ticks.queue = {}
	
	--lib.path.init = ScriptsRootPath .. "data\\init\\"
	ticks.patterns_init = lib.path.init .. "patterns.ini"


	function ticks.callback(v)
		if not v or v.interval ~= INTERVAL_TICK then return end
		local volume = int(v.ds:V(v.ds:Size()))
		local price = v.ds:C(v.ds:Size())
		
		if price and price > 0 and volume > 0 then
			local q = lib:getQuoteLevel2(v.sec_code, v.class_code)
			local tick = ticks:CalculatePressure(
				{
					time = v.time,
					price = price,
					volume = volume,
					sec_code = v.sec_code
				}, q
			)
			ticks:GetSide(tick)
			
			if tick then
				table.sinsert(ticks.queue, tick)
			end
		end
	end
	
	
	function ticks.tick()
		local tick
		local price, datetime
		
		if ticks.clean_size < #ticks.queue then ticks.clean_size = #ticks.queue end
			
		--message(tostring(#ticks.queue))
		
		while #ticks.queue > 0 do
			tick = ticks.queue[1]
			
			table.sremove(ticks.queue, 1)
			
			if not ticks.data[tick.sec_code] then
				if not ticks:load(tick.sec_code)
				then return end
			end
			
			tick = ticks:add(tick)
			
			ticks:CalculateAverage(tick)
			
			if tick then
				for _, f in ipairs(ticks.OnTick) do
					price, datetime = f(tick)
					if price then break end
				end
			end
			
			if price then break end
		end
		
		return price, datetime
	end


	function ticks.save(tick)
		if tick and tick.sec_code then
			local sid = "ticks." .. tick.sec_code
			
			if ticks.saver[tick.sec_code] ~= lib:FormatDate() then
				ticks.saver[tick.sec_code] = lib:FormatDate()
				local file = lib.path.ticks .. ticks.saver[tick.sec_code] .. "." .. tick.sec_code
				
				processor:InitSaver(sid, file)
				processor:save(sid, "")
			end
			
			processor:save(sid, tick.time
				.. "\t" .. tostring(tick.price)
				.. "\t" .. tostring(tick.volume)
				.. "\t" .. tostring(tick.side)
				.. "\t" .. tostring(tick.price_bid)
				.. "\t" .. tostring(tick.volume_bid)
				.. "\t" .. tostring(tick.price_offer)
				.. "\t" .. tostring(tick.volume_offer)
				.. "\t" .. tostring(tick.pressure_bid)
				.. "\t" .. tostring(tick.pressure_offer)
			)
		else
			while #ticks.queue > 0 do
				ticks.save(ticks.queue[1])
				table.sremove(ticks.queue, 1)
			end
		end
	end


	function ticks:load2queue(sec_code, date)
		date = date or lib:FormatDate()
		local file = lib.path.ticks .. date .. "." .. sec_code
		local hf = io.open(file, "r")
		
		if hf then
			local tick
			local line = hf:read()
			
			while line do
				tick = self:line2tick(line, sec_code)
				
				if tick then
					tick.sec_code = sec_code
					table.sinsert(self.queue, tick)
				end
				
				line = hf:read()
			end
			
			hf:close()
		end
		
		message("Ticks loaded " .. #self.queue .. " " .. sec_code .. " " .. date)
	end
	
	
end	-- indicator exclude


end -- of file


